package com.wsoft.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.handler.ICodeRuleHandler;
import com.wsoft.dto.CodeRulesDto;
import com.wsoft.entity.TsCodeRulesDataEntity;
import com.wsoft.entity.TsCodeRulesEntity;
import com.wsoft.enums.RulesTypeEnum;
import com.wsoft.manager.TsCodeRulesDataManager;
import com.wsoft.manager.TsCodeRulesManager;
import com.wsoft.utils.CodeRulesUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author z001
 * @Date 2024/10/18 14:26
 */
@Component
public class CodeRuleHandler implements ICodeRuleHandler {
    @Resource
    private TsCodeRulesManager codeRulesManager;
    @Resource
    private TsCodeRulesDataManager codeRulesDataManager;


    @Override
    public synchronized Object execute(String code, JSONObject formData, Integer isUsed) {
        TsCodeRulesEntity rulesEntity = codeRulesManager.getByCode(code);
        if (null == rulesEntity) {
            return null;
        }
        if (0 == rulesEntity.getType()) {
            //自定义实现类
            try {
                return this.reflectionMethod(rulesEntity, formData, isUsed);
            } catch (Exception e) {
                Asserts.fail("[ts.method.not.exist]");
                throw new RuntimeException(e);
            }
        } else {
            //指定规则
            return this.customRules(rulesEntity, formData, isUsed);
        }
    }

    @Override
    public Object execute(Long id, JSONObject formData, Integer isUsed) {
        TsCodeRulesEntity rulesEntity = codeRulesManager.getById(id);
        return this.execute(rulesEntity.getCode(),formData,isUsed);
    }

    @Override
    public void deleteCode(String code, String value) {
        TsCodeRulesEntity rulesEntity = codeRulesManager.getByCode(code);
        if (rulesEntity.getLength() != value.length()) {
            return;
        }
        List<TsCodeRulesDataEntity> jsonList = codeRulesDataManager.getByRulesJsonList(rulesEntity.getCode(), rulesEntity.getVersion());
        for (TsCodeRulesDataEntity entity : jsonList) {
            if (StrUtil.isNotBlank(entity.getRulesJson())) {
                Map<Integer, String> map = (Map<Integer, String>) JSON.parseObject(entity.getRulesJson(), Map.class);
                //map转正则表达式
                String regex = this.buildRegexFromMap(map);
                //是否匹配当前删除的value值
                if (ReUtil.isMatch(regex, value)) {
                    if (StrUtil.isBlank(entity.getCoverList())) {
                        entity.setCoverList(value);
                    } else {
                        entity.setCoverList(entity.getCoverList() + StrUtil.COMMA + value);
                    }
                }
                codeRulesDataManager.updateById(entity);
                break;
            }
        }
    }

    //############################### 私有函数 ###############################

    /**
     * 反射 执行指定类名下的方法
     *
     * @param rulesEntity
     * @param objectJson
     * @param isUsed
     * @return
     * @throws Exception
     */
    private String reflectionMethod(TsCodeRulesEntity rulesEntity, JSONObject objectJson, Integer isUsed) throws Exception {
        String[] path = extractClassAndMethod(rulesEntity.getClassName());
        // 1. 加载类
        Class<?> clazz = null;
        try {
            clazz = Class.forName(path[0]);
        } catch (ClassNotFoundException e) {
            return null;
        }

        // 2. 创建类的实例
        Object instance = clazz.getDeclaredConstructor().newInstance();

        // 3. 获取方法对象 传入方法名 和方法参数
        String methodName = path[1];
        Class<?>[] parameterTypes = { String.class, JSONObject.class,Integer.class };
        Method method = clazz.getMethod(methodName, parameterTypes);

        // 4. 调用方法
        // 方法需要的参数
        Object[] parameters = {rulesEntity.getCode(), objectJson, isUsed};
        Object result = method.invoke(instance, parameters);
        // 检查结果是否为 null
        if (result == null) {
            return null;
        }

        if(1 == isUsed){
            TsCodeRulesDataEntity ruleData = codeRulesDataManager.getClassValue(rulesEntity.getCode(), objectJson.toJSONString(), rulesEntity.getVersion());
            if(null == ruleData){
                ruleData = new TsCodeRulesDataEntity();
                ruleData.setCode(rulesEntity.getCode());
                ruleData.setVersion(rulesEntity.getVersion());
                ruleData.setType(rulesEntity.getType());
                ruleData.setClassName(rulesEntity.getClassName());
                ruleData.setParams(rulesEntity.getParams());
                ruleData.setParamObject(objectJson.toJSONString());
                ruleData.setSerialNumber(result.toString());
                codeRulesDataManager.save(ruleData);
            }else{
                ruleData.setSerialNumber(result.toString());
                codeRulesDataManager.updateById(ruleData);
            }
        }
        return result.toString();
    }


    /**
     * 自定义规则
     *
     * @param rulesEntity
     * @param objectJson
     * @param isUsed
     * @return
     */
    private String customRules(TsCodeRulesEntity rulesEntity, JSONObject objectJson, Integer isUsed) {
        // 用来存储 影响流水号的值 index
        List<Integer> templateIndex = new ArrayList<>();
        // 用来存储 生成后的值 key:index value:生成的value
        Map<Integer, Object> objectMap = new HashMap<>(16);
        //流水号dto ==> 流水号只考虑一个的情况
        CodeRulesDto serialNumberDto = null;
        //补位dtoList ==> 单个规则有多个补位的情况
        List<CodeRulesDto> coverDtoList = new ArrayList<>();
        List<CodeRulesDto> rulesDtoList = JSONArray.parseArray(rulesEntity.getRules(), CodeRulesDto.class);
        //按照index排序
        rulesDtoList = rulesDtoList.stream()
                .sorted(Comparator.comparing(CodeRulesDto::getIndex))
                .collect(Collectors.toList());
        //遍历规则
        for (CodeRulesDto rulesDto : rulesDtoList) {
            //遍历规则 生成code
            createCodePart(rulesDto, templateIndex, objectJson, objectMap);
            RulesTypeEnum resolve = RulesTypeEnum.resolve(rulesDto.getType());
            if (resolve.getType().equals(RulesTypeEnum.SERIAL_NUMBER.getType())) {
                serialNumberDto = rulesDto;
            }
            if (resolve.getType().equals(RulesTypeEnum.COVER_CONSTANT.getType())) {
                coverDtoList.add(rulesDto);
            }
        }
        //构造最终的code
        return this.assembleCode(rulesEntity, serialNumberDto, coverDtoList, objectMap, templateIndex, isUsed);
    }


    /**
     * 生成 指定规则码
     *
     * @param rulesDto
     * @param templateIndex
     * @param objectJson
     * @param objectMap
     */
    private void createCodePart(CodeRulesDto rulesDto, List<Integer> templateIndex, JSONObject objectJson,
                                Map<Integer, Object> objectMap) {
        RulesTypeEnum resolve = RulesTypeEnum.resolve(rulesDto.getType());
        //根据规则 生成code
        String codePart = resolve.getCode(rulesDto, templateIndex, objectJson);
        //objectMap 存放index 和 生成的codeValue
        objectMap.put(rulesDto.getIndex(), codePart);
    }

    /**
     * 从全路径中 获取 类名 和 方法名
     *
     * @param fullPath
     * @return
     */
    private String[] extractClassAndMethod(String fullPath) {
        // 查找最后一个点的位置，它分隔了类名和方法名
        int lastDotIndex = fullPath.lastIndexOf('.');

        // 确保找到了点，并且前面还有字符（即不是字符串的第一个字符）
        if (lastDotIndex > 0) {
            // 截取类的全路径（从字符串开始到最后一个点之前）
            String className = fullPath.substring(0, lastDotIndex);

            // 截取方法名（从最后一个点之后到字符串末尾）
            String methodName = fullPath.substring(lastDotIndex + 1);

            // 返回类的全路径和方法名作为数组
            return new String[]{className, methodName};
        } else {
            // 如果没有找到点，或者点的位置不正确，则返回一个表示错误的数组
            // 这里我们返回一个包含原始字符串和空字符串的数组，但实际应用中可能需要更严格的错误处理
            return new String[]{fullPath, ""};
        }
    }

    /**
     * 组装code
     *
     * @param objectMap
     */
    private String assembleCode(TsCodeRulesEntity rulesEntity,
                                CodeRulesDto serialNumberDto,
                                List<CodeRulesDto> coverDtoList,
                                Map<Integer, Object> objectMap,
                                List<Integer> templateIndex,
                                Integer isUsed) {
        String codeFormat = null;
        if (null == serialNumberDto) {
            //没有流水号
            codeFormat = assembleCodeNotSerialNumber(objectMap);
        } else {
            //有流水号
            if (StrUtil.isNotBlank(serialNumberDto.getTemplateIndex())) {
                List<String> list = Arrays.asList(serialNumberDto.getTemplateIndex().split(StrUtil.COMMA));
                templateIndex = list.stream().map(Integer::parseInt).collect(Collectors.toList());
            }
            codeFormat = assembleCodeHasSerialNumber(rulesEntity, serialNumberDto, objectMap, templateIndex, isUsed);
        }
        if (CollUtil.isEmpty(coverDtoList)) {
            //没有补位替换 ==> 默认按照长度后面补0
            codeFormat = assembleCoverCodeNone(rulesEntity.getLength(), codeFormat);
        } else {
            //有补位替换
            codeFormat = assembleCoverCodeMany(rulesEntity.getLength(), codeFormat, coverDtoList);
        }
        return codeFormat;

    }


    /**
     * 构建有流水号的code
     *
     * @param code            规则编码表示
     * @param serialNumberDto 流水号 规则dto
     * @param objectMap       key:index value:生成的value
     * @param templateIndex   影响流水号生成的index 集合
     * @return
     */
    private String assembleCodeHasSerialNumber(TsCodeRulesEntity rulesEntity,
                                               CodeRulesDto serialNumberDto,
                                               Map<Integer, Object> objectMap,
                                               List<Integer> templateIndex, Integer isUsed) {

        //生成的模板值 {} 补位 , []流水号
        StringBuilder codeFormat = new StringBuilder();
        Set<Integer> indexSet = objectMap.keySet();
        Map<Integer, String> jsonMap = new HashMap<>(16);
        for (int i = 1; i <= indexSet.size(); i++) {
            String str = objectMap.get(i).toString();
            codeFormat.append(str);
            if (templateIndex.contains(i)) {
                //模板位置
                jsonMap.put(i, str);
            }
        }
        String result = codeFormat.toString();
        //存储data表的rulesJson
        String rulesJson = JSONObject.toJSONString(jsonMap);
        TsCodeRulesDataEntity ruleData = codeRulesDataManager.getByRulesJson(rulesEntity.getCode(), rulesJson, rulesEntity.getVersion());

        if (null == ruleData) {
            //数据库没有值
            String value = CodeRulesUtil.computeLength(serialNumberDto.getLength(), serialNumberDto.getValue(),null);
            result = result.replace(StrUtil.BRACKET_START + StrUtil.BRACKET_END, value);
            if (1 == isUsed) {
                ruleData = new TsCodeRulesDataEntity();
                ruleData.setCode(rulesEntity.getCode());
                ruleData.setRulesJson(rulesJson);
                ruleData.setSerialNumber(value);
                ruleData.setVersion(rulesEntity.getVersion());
                ruleData.setType(rulesEntity.getType());
                ruleData.setClassName(rulesEntity.getClassName());
                ruleData.setParams(rulesEntity.getParams());
                ruleData.setLength(rulesEntity.getLength());
                ruleData.setRules(rulesEntity.getRules());
                codeRulesDataManager.save(ruleData);
            }

        } else {
            //数据库有值
            if (null != serialNumberDto.getComplement() && serialNumberDto.getComplement() == 1 && StrUtil.isNotBlank(ruleData.getCoverList())) {
                //有断码并且需要补码 截取第一个 并返回,同时跟新原数据
                String[] parts = ruleData.getCoverList().split(StrUtil.COMMA);
                result = parts[0];

                // 创建不包含第一个元素的数组切片
                String[] remainingParts = Arrays.copyOfRange(parts, 1, parts.length);
                // 使用逗号连接剩余的元素
                String coverList = String.join(StrUtil.COMMA, remainingParts);
                ruleData.setCoverList(coverList);
            } else {
                String newValue = new BigDecimal(ruleData.getSerialNumber()).add(BigDecimal.ONE).toString();
                newValue = CodeRulesUtil.computeLength(serialNumberDto.getLength(), newValue,null);
                result = result.replace(StrUtil.BRACKET_START + StrUtil.BRACKET_END, newValue);
                ruleData.setSerialNumber(newValue);
            }
            if (1 == isUsed) {
                codeRulesDataManager.updateById(ruleData);
            }
        }
        return result;
    }

    /**
     * 构建 非流水号的code
     *
     * @param objectMap key:index value:生成的value
     * @return
     */
    private String assembleCodeNotSerialNumber(Map<Integer, Object> objectMap) {
        StringBuilder codeSb = new StringBuilder();
        Set<Integer> indexSet = objectMap.keySet();
        for (int i = 1; i <= indexSet.size(); i++) {
            codeSb.append(objectMap.get(i) == null ? StrUtil.EMPTY : objectMap.get(i).toString());
        }
        return codeSb.toString();
    }

    /**
     * 没有补位规则
     * @param codeLength
     * @param codeFormat
     * @return
     */
    private String assembleCoverCodeNone(int codeLength, String codeFormat){
        //没有补位规则的, 按照长度 默认最后面补0
        String coverCode = CodeRulesUtil.PAD_CHAR;
        int formatLength = codeFormat.length();
        if (codeLength == formatLength) {
            return codeFormat;
        } else if (codeLength > formatLength) {
            //补后面
            return StrUtil.padAfter(codeFormat, codeLength, coverCode);
        } else {
            return codeFormat.substring(0, codeLength);
        }
    }

    /**
     * 有补位规则
     * @param codeLength code总长度
     * @param codeFormat 带补位code的
     * @param coverCodeDtoList  补位dto
     * @return
     */
    private String assembleCoverCodeMany(int codeLength, String codeFormat, List<CodeRulesDto> coverCodeDtoList) {
        String coverCode = null;
        //补位个数
        int coverCount = coverCodeDtoList.size();
        //按照部位的个数 平均分配
        int formatLength = codeFormat.length() - 2 * coverCount;
        int[] splitCount = this.splitInteger(codeLength - formatLength, coverCount);
        String[] valueArr = new String[splitCount.length];
        for (int i = 0; i < coverCodeDtoList.size(); i++) {
            CodeRulesDto coverCodeDto =  coverCodeDtoList.get(i);
            coverCode = ObjUtil.isEmpty(coverCodeDto.getValue()) ? CodeRulesUtil.PAD_CHAR : coverCodeDto.getValue().toString();
            String value = this.generateRepeatedString(coverCode, splitCount[i]);
            valueArr[i] = value;
        }
        //替换{}补位符
        return fillPlaceholders(codeFormat,valueArr);
    }



    /**
     * 替换字符串中的{}占位符为指定的值。
     *
     * @param input 输入字符串，包含{}占位符。
     * @param values 用于替换{}占位符的值，按顺序匹配。
     * @return 替换后的新字符串，不包含{}占位符。
     * @throws IllegalArgumentException 如果值的数量与占位符不匹配。
     */
    public  String fillPlaceholders(String input, String... values) {
        // 如果没有提供值或者输入字符串不包含[]，则直接返回输入字符串
        if (values == null || values.length == 0 || !input.contains(StrUtil.DELIM_START + StrUtil.DELIM_END)) {
            return input;
        }
        // 定义正则表达式来匹配[]占位符
        String regex = "\\{\\}";
        // 创建Pattern对象
        Pattern pattern = Pattern.compile(regex);
        // 创建Matcher对象
        Matcher matcher = pattern.matcher(input);

        // 使用StringBuffer来构建结果字符串
        StringBuffer result = new StringBuffer();
        // 用于跟踪当前要填充的值
        int valueIndex = 0;

        // 遍历匹配器找到的所有匹配项
        while (matcher.find()) {
            // 检查是否还有更多的值需要填充
            if (valueIndex < values.length) {
                // 替换当前匹配的[]为相应的值
                matcher.appendReplacement(result, values[valueIndex++]);
            }
        }

        // 添加匹配器之后的任何剩余输入到结果中
        matcher.appendTail(result);
        return result.toString();
    }

    /**
     * 生成重复的字符串直到达到目标长度
     *
     * @param input
     * @param targetLength
     * @return
     */
    public String generateRepeatedString(String input, int targetLength) {
        StringBuilder sb = new StringBuilder();
        while (sb.length() < targetLength) {
            sb.append(input);
        }
        // 如果生成的字符串超过了目标长度，则截取到目标长度
        return sb.substring(0, targetLength);
    }


    /**
     * 根据map 返回一个正则表达式
     *
     * @param map key index,value 固定值
     * @return
     */
    public String buildRegexFromMap(Map<Integer, String> map) {
        if (map == null || map.isEmpty()) {
            // 如果Map为空，则匹配空字符串
            return "^$";
        }
        // 将Map的键转换为List，并排序
        List<Integer> sortedKeys = new ArrayList<>(map.keySet());
        Collections.sort(sortedKeys);

        // 构建正则表达式
        // 正则表达式以^开头
        StringBuilder regexBuilder = new StringBuilder("^");

        for (int i = 0; i < sortedKeys.size(); i++) {
            int key = sortedKeys.get(i);
            // 使用Pattern.quote来转义特殊字符
            String value = Pattern.quote(map.get(key));

            // 如果不是第一个键，则添加.*来表示前一个键和当前键之间的任意字符
            if (i > 0) {
                // 找到前一个键，并计算两个键之间的任意字符序列
                int prevKey = sortedKeys.get(i - 1);
                // 使用.*来表示任意字符 这边不限制两个键之间的字符数量，如要需要限制可以使用{min,max}
                int gap = key - prevKey - 1;
                // 如果间隙大于0，则添加.*
                if (gap > 0) {
                    // 添加任意字符序列
                    regexBuilder.append(".*");
                }
            }
            // 添加当前键对应的值
            regexBuilder.append(value);

        }

        // 最后一个值之后还有字符的情况，这里添加.*
        regexBuilder.append(".*");
        // 正则表达式以$结尾
        regexBuilder.append("$");

        return regexBuilder.toString();
    }



    public    int[] splitInteger(int value, int n) {
        if (n <= 0) {
            throw new IllegalArgumentException("The number of parts must be greater than 0.");
        }

        int[] result = new int[n];
        // 每份的基本数量
        int quotient = value / n;
        // 分完后剩余的数量
        int remainder = value % n;
        // 尽可能平均地分配余数
        for (int i = 0; i < n; i++) {
            result[i] = quotient;
            if (remainder > 0) {
                result[i]++;
                remainder--;
            }
        }
        return result;
    }

    public static void main(String[] args) {

    }

}
